#include "remote/client/tunnelsession.h"

namespace remote {

TunnelClientSession::TunnelClientSession(boost::asio::io_service &io_service,
    TunnelSettings &tunnel_settings,
    SocketPtr local_socket,
    SocketPtr remote_socket)
  : io_service_(io_service),
    tunnel_settings_(tunnel_settings),
    local_socket_(local_socket),
    remote_socket_(remote_socket) {
}

TunnelClientSession::~TunnelClientSession() {
}

bool TunnelClientSession::Start() {
  StartReadLocalData();
  StartReadRemoteData();
  return true;
}

void TunnelClientSession::StartReadLocalData() {
  local_socket_->async_read_some(
    boost::asio::buffer(local_buffer_, DEFAULT_BUFFER_SIZE),
    boost::bind(&TunnelClientSession::HandleLocalRead, shared_from_this(),
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
}

void TunnelClientSession::StartReadRemoteData() {
  remote_socket_->async_read_some(
    boost::asio::buffer(remote_buffer_, DEFAULT_BUFFER_SIZE),
    boost::bind(&TunnelClientSession::HandleRemoteRead, shared_from_this(),
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
}

void TunnelClientSession::Stop() {
}

void TunnelClientSession::HandleLocalRead(
  const boost::system::error_code& err,
  int read_size) {
  if(err) {
    LOG(ERROR) << err.message();
    CloseSession();
    return;
  }
  boost::asio::async_write(*remote_socket_,
                           boost::asio::buffer(local_buffer_, read_size),
                           boost::bind(&TunnelClientSession::HandleRemoteWrite,
                                       shared_from_this(),
                                       boost::asio::placeholders::error));
}

void TunnelClientSession::HandleLocalWrite(
  const boost::system::error_code& err) {
  if(err) {
    LOG(ERROR) << err.message();
    CloseSession();
    return;
  }
  StartReadRemoteData();
}

//
void TunnelClientSession::HandleRemoteRead(
  const boost::system::error_code& err,
  int read_size) {
  if(err) {
    LOG(ERROR) << err.message();
    CloseSession();
    return;
  }
  boost::asio::async_write(*local_socket_,
                           boost::asio::buffer(remote_buffer_, read_size),
                           boost::bind(&TunnelClientSession::HandleLocalWrite,
                                       shared_from_this(),
                                       boost::asio::placeholders::error));
}

void TunnelClientSession::HandleRemoteWrite(
  const boost::system::error_code& err) {
  if(err) {
    LOG(ERROR) << err.message();
    CloseSession();
    return;
  }
  StartReadLocalData();
}

void TunnelClientSession::CloseSession() {
  if(local_socket_) {
    local_socket_->shutdown(boost::asio::ip::tcp::socket::shutdown_both);
    local_socket_->close();
    local_socket_.reset();
  }
  if(remote_socket_) {
    remote_socket_->shutdown(boost::asio::ip::tcp::socket::shutdown_both);
    remote_socket_->close();
    remote_socket_.reset();
  }
}

}